package com.github.ecbp.user.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.ecbp.common.constant.CacheKeyConstant;
import com.github.ecbp.common.data.entity.BaseEntity;
import com.github.ecbp.common.data.service.impl.BaseServiceImpl;
import com.github.ecbp.common.data.utils.TransformUtils;
import com.github.ecbp.common.req.BaseIdDTO;
import com.github.ecbp.common.resp.BaseVO;
import com.github.ecbp.common.security.utils.CommonAdminUtils;
import com.github.ecbp.common.utils.Asserts;
import com.github.ecbp.user.service.common.UserCodeAndMessageEnum;
import com.github.ecbp.user.service.dto.ButtonBo;
import com.github.ecbp.user.service.dto.MenuBo;
import com.github.ecbp.user.service.dto.MenuDto;
import com.github.ecbp.user.service.dto.MenuQueryDto;
import com.github.ecbp.user.service.entity.TMenu;
import com.github.ecbp.user.service.entity.TRole;
import com.github.ecbp.user.service.entity.TRoleMenuRelation;
import com.github.ecbp.user.service.mapper.MenuMapper;
import com.github.ecbp.user.service.response.MenuRes;
import com.github.ecbp.user.service.response.PermissionRes;
import com.github.ecbp.user.service.service.*;
import com.github.ecbp.user.service.vo.AdminRoleIdVo;
import com.github.ecbp.user.service.vo.ButtonNode;
import com.github.ecbp.user.service.vo.MenuNode;
import com.github.ecbp.user.service.vo.MenuPageVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 后台菜单表 服务实现类
 * </p>
 *
 * @author zj2626
 * @since 2020-03-18
 */
@Service
public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, TMenu> implements MenuService {
    @Autowired
    private RoleService roleService;
    @Autowired
    private AdminRoleRelationService adminRoleRelationService;
    @Autowired
    private RoleMenuRelationService roleMenuRelationService;
    @Autowired
    private PermissionMenuRelationService permissionMenuRelationService;

    @Override
    public boolean create(MenuDto param) {
        TMenu menu = TransformUtils.copy(param, TMenu.class);
        updateLevel(menu);
        return super.saveBase(menu);
    }

    @Override
    public IPage<MenuPageVo> page(MenuQueryDto param) {
        IPage<MenuPageVo> iPage = super.page(param, MenuPageVo.class);
        if (!CollectionUtils.isEmpty(iPage.getRecords())) {
            final Set<Long> ids = iPage.getRecords().stream().map(BaseVO::getId).collect(Collectors.toSet());

            LambdaQueryWrapper<TMenu> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(TMenu::getId, TMenu::getParentId);
            wrapper.in(TMenu::getParentId, ids);
            List<TMenu> tMenus = super.list(wrapper);
            if (CollectionUtils.isEmpty(tMenus)) {
                tMenus = new ArrayList<>(1);
            }
            final Map<Long, Long> countting = tMenus.stream().collect(Collectors.groupingBy(TMenu::getParentId, Collectors.counting()));

            final List<PermissionRes> permissionVoByMenuId = permissionMenuRelationService.getPermissionVoByMenuId(ids);
            final Map<Long, List<PermissionRes>> collect =
                    permissionVoByMenuId.stream().collect(Collectors.groupingBy(PermissionRes::getMenuId));
            iPage.getRecords().forEach(record -> {
                final List<PermissionRes> permissionRes = collect.get(record.getId());
                if (!CollectionUtils.isEmpty(permissionRes)) {
                    record.setPermissions(collect.get(record.getId()));
                } else {
                    record.setPermissions(Collections.emptyList());
                }

                final Long aLong = countting.get(record.getId());
                record.setHasSub(null != aLong && 0 != aLong);
            });
        }
        return iPage;
    }

    /**
     * 修改菜单层级
     */
    private void updateLevel(TMenu param) {
        if (param.getParentId() == 0) {
            //没有父菜单时为一级菜单
            param.setLevel(0);
        } else {
            //有父菜单时选择根据父菜单level设置
            TMenu parentMenu = baseMapper.selectById(param.getParentId());
            if (parentMenu != null) {
                param.setLevel(parentMenu.getLevel() + 1);
            } else {
                param.setLevel(0);
            }
        }
    }

    @Override
    public int updateSelectiveById(MenuDto param) {
        TMenu menu = TransformUtils.copy(param, TMenu.class);
        menu.setId(param.getId());
        updateLevel(menu);
        int result = baseMapper.updateById(menu);
        Asserts.checkNotNull(result, UserCodeAndMessageEnum.MENU_SAVE_ERROR);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public boolean removeMenuById(BaseIdDTO param) {
        Asserts.isTrue(removeById(param.getId()), UserCodeAndMessageEnum.DELETE_MENU_EXCEPTION);

        LambdaQueryWrapper<TMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(TMenu::getId);
        wrapper.eq(TMenu::getParentId, param.getId());
        super.remove(wrapper);
        return false;
    }

    /**
     * 获取所有菜单
     *
     * @return
     */
    @Override
    public List<MenuNode> treeList(MenuBo menuBo) {
        List<TMenu> menuList;
        // 需要获取当前用户拥有权限的菜单(即其角色下的菜单)
        if (0 == menuBo.getAccessType()) {
            menuList = getMenuList(CommonAdminUtils.getLoginIdFromSystem());
        } else if (1 == menuBo.getAccessType()) {
            menuList = getMenuListByRoleId(menuBo.getRoleId());
        } else {
            menuList = baseMapper.selectList(new QueryWrapper<>());
        }
        if (CollectionUtils.isEmpty(menuList)) {
            return Collections.emptyList();
        }

        // 类型转换, 结构转为树状
        List<MenuNode> result = menuList.stream()
                .filter(menu -> menu.getParentId().equals(0L))
                .map(menu -> covertMenuNode(menu, menuList)).collect(Collectors.toList());

        // 需要拉取菜单下按钮
        if (!CollectionUtils.isEmpty(menuList) && menuBo.getCarryButton()) {
            Set<Long> menuIds = menuList.stream().map(BaseEntity::getId).collect(Collectors.toSet());
            List<ButtonNode> buttonNodeList = getPermissionByMenuList(menuIds);
            if (!CollectionUtils.isEmpty(buttonNodeList)) {
                // 需要获取当前用户拥有权限的按钮(即其权限匹配的按钮)
                if (0 == menuBo.getAccessType()) {
                    Iterator<ButtonNode> buttonNodeIterator = buttonNodeList.iterator();
                    PathMatcher pathMatcher = new AntPathMatcher(CacheKeyConstant.PATH_SEPARATOR);
                    while (buttonNodeIterator.hasNext()) {
                        ButtonNode buttonNode = buttonNodeIterator.next();
                        long size = menuBo.getUserDetails().getGrantedAuthorities().stream()
                                .filter(adminPermission ->
                                        pathMatcher.match(((GrantedAuthority) adminPermission).getAuthority(), buttonNode.getPower()))
                                .count();
                        if (size == 0) {
                            buttonNodeIterator.remove();
                        }
                    }
                }
                // 按钮挂在菜单下
                result = result.stream()
                        .map(menu -> covertButtonNode(menu, buttonNodeList)).collect(Collectors.toList());
            }
        }
        return result;
    }

    private List<TMenu> getMenuListByRoleId(Long roleId) {
        LambdaQueryWrapper<TRoleMenuRelation> roleMenuRelationLambdaQueryWrapper = new LambdaQueryWrapper<>(TRoleMenuRelation.class);
        roleMenuRelationLambdaQueryWrapper.select(TRoleMenuRelation::getMenuId);
        roleMenuRelationLambdaQueryWrapper.eq(TRoleMenuRelation::getRoleId, roleId);
        List<TRoleMenuRelation> roleMenuRelations = roleMenuRelationService.list(roleMenuRelationLambdaQueryWrapper);

        if (!CollectionUtils.isEmpty(roleMenuRelations)) {
            LambdaQueryWrapper<TMenu> wrapper = new LambdaQueryWrapper<>(TMenu.class);
            wrapper.in(BaseEntity::getId, roleMenuRelations.stream().map(TRoleMenuRelation::getMenuId).collect(Collectors.toSet()));
            return baseMapper.selectList(wrapper);
        }

        return null;
    }

    @Override
    public List<TMenu> getMenuList(Long adminId) {
        if (null == adminId) {
            return Collections.emptyList();
        }
        List<AdminRoleIdVo> roles = adminRoleRelationService.getRoleIdByAdminId(adminId);
        if (!CollectionUtils.isEmpty(roles)) {
            Set<Long> roleIds = roles.stream().map(AdminRoleIdVo::getRoleId).collect(Collectors.toSet());
            List<TMenu> menuList = roleMenuRelationService.getMenuByRoleId(roleIds);
            return new ArrayList<>(menuList);
        }

        return Collections.emptyList();
    }


    /**
     * 获取菜单下所有的按钮
     *
     * @return
     */
    @Override
    public List<ButtonNode> buttonList(ButtonBo buttonBo) {
        List<ButtonNode> buttonNodeList = getPermissionByMenuList(Collections.singleton(buttonBo.getMenuId()));
        if (!CollectionUtils.isEmpty(buttonNodeList)) {
            // 需要获取当前用户拥有权限的按钮(即其权限匹配的按钮)
            if (buttonBo.getAuthorityAccess()) {
                Iterator<ButtonNode> buttonNodeIterator = buttonNodeList.iterator();
                while (buttonNodeIterator.hasNext()) {
                    ButtonNode buttonNode = buttonNodeIterator.next();
                    PathMatcher pathMatcher = new AntPathMatcher(CacheKeyConstant.PATH_SEPARATOR);
                    long size = buttonBo.getUserDetails().getGrantedAuthorities().stream()
                            .filter(adminPermission ->
                                    pathMatcher.match(((GrantedAuthority) adminPermission).getAuthority(), buttonNode.getPower()))
                            .count();
                    if (size == 0) {
                        buttonNodeIterator.remove();
                    }
                }
            }
        }

        return buttonNodeList;
    }

    @Override
    public MenuRes getById(Long id) {
        return TransformUtils.copy(baseMapper.selectById(id), MenuRes.class);
    }

    private List<ButtonNode> getPermissionByMenuList(Set<Long> menuIds) {
        List<PermissionRes> permissionMenuRelations = permissionMenuRelationService.getPermissionVoByMenuId(menuIds);
        if (!CollectionUtils.isEmpty(permissionMenuRelations)) {
            return TransformUtils.listCopy(permissionMenuRelations, ButtonNode.class);
        }
        return Collections.emptyList();
    }

    /**
     * 将menu转化为MenuNode并设置children属性
     */
    private MenuNode covertMenuNode(TMenu menu, List<TMenu> menuList) {
        MenuNode node = TransformUtils.copy(menu, MenuNode.class);
        node.setId(menu.getId());
        List<MenuNode> children = menuList.stream()
                .filter(subMenu -> subMenu.getParentId().equals(menu.getId()))
                .map(subMenu -> covertMenuNode(subMenu, menuList)).collect(Collectors.toList());
        node.setChildren(children);
        return node;
    }

    /**
     * 将ButtonNode设置到childrenButton属性
     */
    private MenuNode covertButtonNode(MenuNode menu, List<ButtonNode> buttonList) {
        List<ButtonNode> childButton = buttonList.stream()
                .filter(subButton -> subButton.getMenuId().equals(menu.getId())).collect(Collectors.toList());
        menu.setChildButton(childButton);
        menu.getChildren().stream()
                .map(subMenu -> covertButtonNode(subMenu, buttonList)).collect(Collectors.toList());
        return menu;
    }

    /**
     * 重新加载权限相关的用户的权限信息
     *
     * @param id
     */
    @Override
    public void reloadUserPermission(Long id) {
        final List<TRole> roleByMenuId = roleMenuRelationService.getRoleByMenuId(id);
        if (!CollectionUtils.isEmpty(roleByMenuId)) {
            Set<Long> roleIds = roleByMenuId.stream().map(BaseEntity::getId).collect(Collectors.toSet());
            roleService.reloadUserPermission(roleIds);
        }
    }

    @Override
    protected void assemble(TMenu entity, LambdaQueryWrapper<TMenu> wrapper) {
        super.assemble(entity, wrapper);
        if (null != entity.getParentId()) {
            wrapper.eq(TMenu::getParentId, entity.getParentId());
        }
        if (null != entity.getLevel()) {
            wrapper.eq(TMenu::getLevel, entity.getLevel());
        }
        if (!StringUtils.isEmpty(entity.getTitle())) {
            wrapper.likeRight(TMenu::getTitle, entity.getTitle());
        }
        if (!StringUtils.isEmpty(entity.getName())) {
            wrapper.likeRight(TMenu::getName, entity.getName());
        }
        wrapper.orderByDesc(BaseEntity::getCreateTime);
    }
}
